home *** CD-ROM | disk | FTP | other *** search
- {*********************************************************}
- {* AACvRomn *}
- {* Copyright (c) Julian M Bucknall 1998 *}
- {* All rights reserved. *}
- {*********************************************************}
- {* Convert integers to/from Roman numbers *}
- {*********************************************************}
-
- {Note: this unit is released as freeware. In other words, you are free
- to use this unit in your own applications, however I retain all
- copyright to the code. JMB}
-
- unit AACvRomn;
-
- interface
-
- function IntToRoman(aValue : integer) : string;
- {-convert an integer between 1 and 3999 to a Roman number}
-
- function RomanToInt(aValue : string) : integer;
- {-convert Roman number to an integer}
-
- implementation
-
- uses
- SysUtils;
-
- const
- RomanDigits : array [1..9] of string[4] =
- ('I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX');
- Roman10s : array [1..9] of string[4] =
- ('X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC');
- Roman100s : array [1..9] of string[4] =
- ('C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM');
- Roman1000s : array [1..3] of string[3] =
- ('M', 'MM', 'MMM');
-
- RomanNumerals : string[7] = 'MDCLXVI';
- {-The individual Roman numerals}
-
- RomanStateMc : array [0..17, 1..7] of word =
- {Numeral: M D C L X V I}
- {State 0:} ((32001, 16004, 3205, 1609, 330, 174, 47),
- { 1:} (32002, 16004, 3205, 1609, 330, 174, 47),
- { 2:} (32003, 16004, 3205, 1609, 330, 174, 47),
- { 3:} ( 0, 16004, 3205, 1609, 330, 174, 47),
- { 4:} ( 0, 0, 3206, 1609, 330, 174, 47),
- { 5:} (25608, 9608, 3207, 1609, 330, 174, 47),
- { 6:} ( 0, 0, 3207, 1609, 330, 174, 47),
- { 7:} ( 0, 0, 3208, 1609, 330, 174, 47),
- { 8:} ( 0, 0, 0, 1609, 330, 174, 47),
- { 9:} ( 0, 0, 0, 0, 331, 174, 47),
- {10:} ( 0, 0, 2573, 973, 332, 174, 47),
- {11:} ( 0, 0, 0, 0, 332, 174, 47),
- {12:} ( 0, 0, 0, 0, 333, 174, 47),
- {13:} ( 0, 0, 0, 0, 0, 174, 47),
- {14:} ( 0, 0, 0, 0, 0, 0, 48),
- {15:} ( 0, 0, 0, 0, 287, 127, 49),
- {16:} ( 0, 0, 0, 0, 0, 0, 49),
- {17:} ( 0, 0, 0, 0, 0, 0, 63));
- {-State machine table to convert from Roman numbers to integers.
- Each entry is equal to (ValueToAdd * 32) + NextState for a
- given State/Roman numeral.
- State 0 is the initial state; state 31 is the terminator state.}
-
-
- {===Helper routines==================================================}
- procedure RaiseBadNumberError(aValue : integer);
- begin
- raise EConvertError.Create(
- Format('IntToRoman: cannot convert %d to Roman value', [aValue]));
- end;
- {--------}
- procedure RaiseEmptyStringError;
- begin
- raise EConvertError.Create('RomanToInt: string is empty');
- end;
- {--------}
- procedure RaiseBadCharError(const S : string; C : char; aPosn : integer);
- begin
- raise EConvertError.Create(
- Format('RomanToInt: unknown character %s at position %d in "%s"',
- [C, aPosn, S]));
- end;
- {--------}
- procedure RaiseBadRomanNumberError(const S : string; aPosn : integer);
- begin
- raise EConvertError.Create(
- Format('RomanToInt: "%s" is a badly formed Roman number at position %d',
- [S, aPosn]));
- end;
- {====================================================================}
-
-
- {===Interfaced routines==============================================}
- function IntToRoman(aValue : integer) : string;
- var
- Digit : integer;
- begin
- if (aValue <= 0) or (aValue >= 4000) then
- RaiseBadNumberError(aValue);
- {get 1000s digit and convert}
- Digit := aValue div 1000;
- if (Digit <> 0) then
- Result := Roman1000s[Digit]
- else
- Result := '';
- {get 100s digit and convert}
- aValue := aValue mod 1000;
- Digit := aValue div 100;
- if (Digit <> 0) then
- Result := Result + Roman100s[Digit];
- {get 10s digit and convert}
- aValue := aValue mod 100;
- Digit := aValue div 10;
- if (Digit <> 0) then
- Result := Result + Roman10s[Digit];
- {get singles digit and convert}
- Digit := aValue mod 10;
- if (Digit <> 0) then
- Result := Result + RomanDigits[Digit];
- end;
- {--------}
- function RomanToInt(aValue : string) : integer;
- var
- i : integer;
- ChInx : integer;
- State : integer;
- StateValue : integer;
- Ch : char;
- begin
- {we don't like empty strings}
- if (aValue = '') then
- RaiseEmptyStringError;
- {initialise sum as zero; set initial state to 0}
- Result := 0;
- State := 0;
- {for each letter in the string...}
- for i := 1 to length(aValue) do begin
- {if we're in the terminator state, we shouldn't get any more Roman
- numerals, hence raise error}
- if (State = 31) then
- RaiseBadRomanNumberError(aValue, i);
- {get the next character, check to see if it's valid}
- Ch := UpCase(aValue[i]);
- ChInx := Pos(Ch, RomanNumerals);
- if (ChInx = 0) then
- RaiseBadCharError(aValue, aValue[i], i);
- {get the value to add, if it's zero we've got a badly formed Roman
- number}
- StateValue := RomanStateMc[State, ChInx];
- if (StateValue = 0) then
- RaiseBadRomanNumberError(aValue, i);
- {increment the sum, set the next state}
- inc(Result, StateValue div 32);
- State := StateValue mod 32;
- end;
- end;
- {====================================================================}
-
- end.
-